#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>

#define BOOST_TEST_MODULE PtreeExamples 
#include <boost/test/unit_test.hpp>
#define WFORMAT(fmt, parameters)\
	boost::str(boost::wformat(fmt) % parameters)

#define PUT(root, parameter)\
{\
	std::wstring field;\
	if (jsonValue.m_field.empty())\
		field = WFORMAT(L"%s.%s", L###root % L###parameter);\
	else\
		field = WFORMAT(L"%s.%s", jsonValue.m_field % L###parameter);\
	JsonValue jv(pt, field);\
	parameter >> jv;\
}
#define GET(root, parameter)\
{\
	std::wstring field;\
	if (jsonValue.m_field.empty())\
		field = WFORMAT(L"%s.%s", L###root % L###parameter);\
	else\
		field = WFORMAT(L"%s.%s", jsonValue.m_field % L###parameter);\
	JsonValue jv(pt, field);\
	parameter << jv;\
}
class JsonValue
{
public:
	JsonValue(boost::property_tree::wptree& pt, const std::wstring& field)
		: m_pt(pt)
		, m_field(field)
	{
	}
	boost::property_tree::wptree& m_pt;
	const std::wstring& m_field;
};
template<typename T>
void operator>>(const T& t, JsonValue& jsonValue)
{
	jsonValue.m_pt.put(jsonValue.m_field, t);
}
template<typename T>
void operator>>(const std::vector<T>& v, JsonValue& jsonValue)
{
	boost::property_tree::wptree ptree;
	BOOST_FOREACH(const T& t, v) {
		boost::property_tree::wptree child;
		JsonValue jv(child, L"");
		t >> jv;
		ptree.push_back(std::make_pair(L"", child));
	}
	jsonValue.m_pt.add_child(jsonValue.m_field, ptree);
}
template<typename T>
void operator<<(T& t, JsonValue& jsonValue)
{
	t = jsonValue.m_pt.get<T>(jsonValue.m_field);
}
template<typename T>
void operator<<(std::vector<T>& v, JsonValue& jsonValue)
{
	boost::property_tree::wptree& pt = jsonValue.m_pt.get_child(jsonValue.m_field);
	BOOST_FOREACH(boost::property_tree::wptree::value_type& i, pt) {
		T t;
		boost::property_tree::wptree& pt = i.second;
		JsonValue jv(pt, L"");
		t << jv;
        	v.push_back(t);
	}
}
#define JSONSERIALIZATION(root, puts, gets)\
public:\
	void operator>>(JsonValue& jsonValue) const\
	{\
		boost::property_tree::wptree& pt = jsonValue.m_pt;\
		puts;\
	}\
	void operator<<(JsonValue& jsonValue)\
	{\
		boost::property_tree::wptree& pt = jsonValue.m_pt;\
		gets;\
	}\
	void operator>>(std::wostream& out)\
	{\
		boost::property_tree::wptree pt;\
		JsonValue jsonValue(pt, L"");\
		(*this) >> jsonValue;\
		boost::property_tree::json_parser::write_json(out, pt);\
	}\
	void operator<<(std::wistream& in)\
	{\
		boost::property_tree::wptree pt;\
		boost::property_tree::json_parser::read_json(in, pt);\
		JsonValue jsonValue(pt, L"");\
		(*this) << jsonValue;\
	}
#define JSONSERIALIZATION2(root, p0, p1)\
	JSONSERIALIZATION(root,\
		PUT(root, p0);\
		PUT(root, p1);,\
		GET(root, p0);\
		GET(root, p1);)
#define JSONSERIALIZATION3(root, p0, p1, p2)\
	JSONSERIALIZATION(root,\
		PUT(root, p0);\
		PUT(root, p1);\
		PUT(root, p2);,\
		GET(root, p0);\
		GET(root, p1);\
		GET(root, p2);)
struct Profile
{
	std::wstring id;
	std::wstring nickname;
	int age;
	JSONSERIALIZATION3(Profile, id, nickname, age);
	/*
	void operator>>(JsonValue& jsonValue)
	{
		boost::property_tree::wptree& pt = jsonValue.m_pt;
		PUT(profile, id);
		PUT(profile, nickname);
		PUT(profile, age);
	}
	void operator<<(JsonValue& jsonValue)
	{
		boost::property_tree::wptree& pt = jsonValue.m_pt;
		GET(profile, id);
		GET(profile, nickname);
		GET(profile, age);
	}
	void operator>>(std::wostream& out)
	{
		boost::property_tree::wptree pt;
		JsonValue jsonValue(pt, L"");
		(*this) >> jsonValue;	
		boost::property_tree::json_parser::write_json(out, pt);
	}
	void operator<<(std::wistream& in)
	{
		boost::property_tree::wptree pt;
		boost::property_tree::json_parser::read_json(in, pt);
		JsonValue jsonValue(pt, L"");
		(*this) << jsonValue;	
	}
	*/
};
struct Item
{
	bool operator==(const Item& item)
	{
		return name == item.name && index == item.index;
	}
	std::wstring name;
	int index;
	JSONSERIALIZATION2(Item, name, index);
};
struct User
{
	Profile profile;
	std::vector<Item> items;
	JSONSERIALIZATION2(User, profile, items);
	/*
	void operator>>(JsonValue& jsonValue)
	{
		boost::property_tree::wptree& pt = jsonValue.m_pt;
		PUT(user, profile);
		PUT(user, items);
	}
	void operator<<(JsonValue& jsonValue)
	{
		boost::property_tree::wptree& pt = jsonValue.m_pt;
		GET(user, profile);
		GET(user, items);
	}
	void operator>>(std::wostream& out)
	{
		boost::property_tree::wptree pt;
		JsonValue jsonValue(pt, L"");
		(*this) >> jsonValue;	
		boost::property_tree::json_parser::write_json(out, pt);
	}
	void operator<<(std::wistream& in)
	{
		boost::property_tree::wptree pt;
		boost::property_tree::json_parser::read_json(in, pt);
		JsonValue jsonValue(pt, L"");
		(*this) << jsonValue;	
	}
	*/
};
BOOST_AUTO_TEST_CASE(JSONSERIALIZATION)
{
#ifdef _DEBUG
	std::wcout << L"debug" << std::endl;
#endif
#ifdef NDEBUG
	std::wcout << L"release" << std::endl;
#endif
	{
		User user;
		user.profile.id = L"elvis";
		user.profile.nickname = L"ㄚ達";
		user.profile.age = 36;
		Item item;
		item.name = L"寶物一";
		item.index = 0;
		user.items.push_back(item);
		item.name = L"寶物二";
		item.index = 1;
		user.items.push_back(item);
		item.name = L"寶物三";
		item.index = 2;
		user.items.push_back(item);

		{
			std::wofstream out("user.json");
			user >> out;
		}
		{
			Profile profile;
			profile.id = L"elvis";
			profile.nickname = L"ㄚ達";
			profile.age = 36;
			std::wofstream out("profile.json");
			profile >> out;
		}
	}
	{
		std::vector<Item> items;
		Item item;
		item.name = L"寶物一";
		item.index = 0;
		items.push_back(item);
		item.name = L"寶物二";
		item.index = 1;
		items.push_back(item);
		item.name = L"寶物三";
		item.index = 2;
		items.push_back(item);

		std::wifstream in("user.json");
		User user;
		user << in;
		BOOST_CHECK(L"elvis" == user.profile.id);
		BOOST_CHECK(36 == user.profile.age);
		BOOST_CHECK(L"ㄚ達" == user.profile.nickname);
		//BOOST_CHECK(items == user.items);
	}
}
BOOST_AUTO_TEST_CASE(Using_wstring_version)
{
	std::wstringstream in(L"\u4E00 value"); 
	boost::property_tree::wptree wpt; 
	BOOST_REQUIRE_NO_THROW(boost::property_tree::read_info(in, wpt)); 
	std::wcout << wpt.get<std::wstring>(L"\u4E00") << std::endl;
}

